// 1.获取对象类型中的可选属性的联合类型
type ExcludeUndefined<T> = { [K in keyof T]: Exclude<T[K], undefined> }; // 排除undefined

export type OptionalKeys<T, K = keyof T> = K extends keyof T
  ? undefined extends ExcludeUndefined<T>[K]
    ? K
    : never
  : never;

type OptionalKeys1<T, K = keyof T> = K extends keyof T // K 联合类型 'foo'|'bar'
  ? Omit<T, K> extends T // 这里的K是遍历的单个数据，T排除了K属性后依然和T属性保持一样的功能（参考一下例子）
    ? K
    : never
  : never;
type d = Omit<{ foo: number; bar?: string }, "bar">; // 从T中排除掉属性名为K的属性

type test1 = { foo: number; bar?: string; flag: number };
type test2 = { foo: number; flag: number };
type test3 = { flag: number };

let example1: test1 = {} as any;
let example2: test2 = {} as any;
let example3: test3 = {} as any;
// example1 = example2; // 正确
// example1 = example3; // 报错了

type Oa1 = OptionalKeys<{
  foo: number | undefined;
  bar?: string;
  flag: boolean;
}>; // bar
type Oa2 = OptionalKeys1<{ foo: number; bar?: string }>; // bar
type Oa3 = OptionalKeys<{ foo: number; flag: boolean }>; // never
type Oa4 = OptionalKeys<{ foo?: number; flag?: boolean }>; // foo|flag
type Oa5 = OptionalKeys<{}>;

type g = Exclude<"a" | "1" | "2", "a" | "y" | "z">; //'1' | '2'
type j = ExcludeUndefined<{ foo: number | undefined; flag?: boolean }>;

//2.保留一个对象中的可选属性类型
//Pick<T, K>从类型 T 中选择出属性 K，构造成一个新的类型。
type PickOptional<T> = Pick<T, OptionalKeys<T>>;
type a1 = PickOptional<{
  foo: number | undefined;
  bar?: string;
  flag: boolean;
}>; // {bar?:string|undefined}
type a2 = PickOptional<{ foo: number; bar?: string }>; // {bar?:string}
type a3 = PickOptional<{ foo: number; flag: boolean }>; // {}
type a4 = PickOptional<{ foo?: number; flag?: boolean }>; // {foo?:number,flag?:boolean}
type a5 = PickOptional<{}>; // {}

//3.获取对象类型中的必须属性的联合类型
type RequiredKeys<T, K = keyof T> = K extends keyof T
  ? undefined extends ExcludeUndefined<T>[K]
    ? never
    : K
  : never;
type Ra1 = RequiredKeys<{
  foo: number | undefined;
  bar?: string;
  flag: boolean;
}>; // foo|flag
type Ra2 = RequiredKeys<{ foo: number; bar?: string }>; // foo
type Ra3 = RequiredKeys<{ foo: number; flag: boolean }>; // foo|flag
type Ra4 = RequiredKeys<{ foo?: number; flag?: boolean }>; // never
type Ra5 = RequiredKeys<{}>; // never

// 4. 保留一个对象中的必须属性
type PickRequired1<T> = Omit<T, OptionalKeys<T>>;
type PickRequired<T> = Pick<T, RequiredKeys<T>>;
type Pa1 = PickRequired<{
  foo: number | undefined;
  bar?: string;
  flag: boolean;
}>; // {foo:number|undefined,flag:boolean}
type Pa2 = PickRequired<{ foo: number; bar?: string }>; // {foo:number}
type Pa3 = PickRequired<{ foo: number; flag: boolean }>; // {foo:number,flag:boolean}
type Pa4 = PickRequired<{ foo?: number; flag?: boolean }>; // {}
type Pa5 = PickRequired<{}>; // {}

// 5. 合并两个对象类型T以及K，如果属性重复，则以K中属性类型为准；
type obj1 = {
  el: string;
  age: number;
};

type obj2 = {
  el: HTMLElement;
  flag: boolean;
};

type Merge<T, K> = {
  [k in Exclude<keyof T, keyof K>]: T[k];
} & K;
type Merge1<T, K> = Pick<T, Exclude<keyof T, keyof K>> & // 拿到T中没有和K重复的属性，也就是age:number
  K; // 再合并K
type obj3 = Merge1<obj1, obj2>; // {el:HtmlElement,age:number,flag:boolean}

type obj5<T> = T extends keyof obj1 ? T : never;
type obj4 = Pick<obj1, obj5<keyof obj2>>;
const a = { ...({} as obj3) };
console.log(a.el.scrollTop, a.age.toFixed(0), a.flag.valueOf());
// console.log(a.el.charAt(0))     // error

interface User {
  age: number;
  name: string;
  adress: string;
}
type PickUser = Pick<User, "age" | "name">;

//6.判断是否为never类型
// 任何类型都不能赋给never，只有never可以
type IsNever<T> = [T] extends [never] ? true : false;
type A = IsNever<never>; // true
type B = IsNever<string>; // false
type C = IsNever<undefined>; // false
type D = IsNever<any>; // false

//7.判断是否为没有属性的对象类型{}
// type IsEmptyType<T> = T extends Record<string, {}> ? true : false

type IsEmptyType<T> = number extends T // 排除null undefined never...
  ? keyof T extends never //可以排除掉number，string等
    ? T extends {}
      ? true
      : false
    : false
  : false;

// 数字，字符串和布尔值也是对象
type IA = IsEmptyType<string>; // false
type IB = IsEmptyType<{ a: 3 }>; // false
type IC = IsEmptyType<{}>; // true
type ID = IsEmptyType<any>; // false
type IE = IsEmptyType<object>; // false
type IF = IsEmptyType<Object>; // false
type TG = IsEmptyType<unknown>; // false

type h = number extends undefined ? 1 : 2;
let ea: {} = 1;
type stringtest = keyof string; //number | typeof Symbol.iterator | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | ... 34 more ... | "trimEnd"
type nulltest = keyof null; // never
type undefinedtest = keyof undefined; // never
type objtest = keyof {}; // never
type anytest = keyof any; // string | number | symbol
type test = number extends unknown ? 1 : 2;

// 8. 判断是否为any类型
// 使用 [T] 避免传入的是联合类型导致类型分布
// unknown 只能赋值给 any或者unknown
// any可以赋值给string，但是unknown不可以赋值给string
type IsAny<T> = [unknown] extends [T]
  ? [T] extends [string]
    ? true
    : false
  : false;

type AA = IsAny<string>; // false
type AB = IsAny<any>; // true
type AC = IsAny<unknown>; // false
type AD = IsAny<never>; // false

// 9. 实现Connect类型，能够自动地转化Redux Module对象中的函数类型

interface Module {
  count: number;
  message: string;
  asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
  syncMethod<T, U>(action: Action<T>): Action<U>;
}

interface Action<T> {
  payload?: T;
  type: string;
}

// 这个要求的结果
type Result = {
  asyncMethod<T, U>(input: T): Action<U>;
  syncMethod<T, U>(action: T): Action<U>;
};
// M 类型为Function的属性名称
type M<T> = {
  [k in keyof T]: k extends string
    ? T[k] extends Function
      ? k
      : never
    : never;
}[keyof T];
type ff = M<Module>;
type InferConnectFunctionParameterType<Fun> = Fun extends <T, U>(
  input: Promise<T>
) => Promise<Action<U>>
  ? <T, U>(input: T) => Action<U>
  : Fun extends <T, U>(action: Action<T>) => Action<U>
  ? <T, U>(action: T) => Action<U>
  : never;

type Connect<
  T,
  M extends string = {
    [k in keyof T]: k extends string
      ? T[k] extends Function
        ? k
        : never
      : never;
  }[keyof T]
> = {
  [k in M]: k extends keyof T ? InferConnectFunctionParameterType<T[k]> : never;
};
type Ce = Connect<Module>;
// 实现类型Connect，要求 Connect<Module> 的结果为上面的 Result
// 只要函数类型的属性；
// 如果函数是异步函数，要求自动解析出来Promise中的类型；

// 10. 有且只有一个属性
// 实现一个叫做 UnionToBooleanProps 的泛型，使得以下需求成立
type UnionToBooleanProps<
  T extends string,
  TT extends string = T
> = T extends any
  ? { [k in Exclude<TT, T>]?: void } & { [k in T]: boolean }
  : never;

type MessageStringType = "info" | "success" | "warning" | "error";
type OneMessageTypes = UnionToBooleanProps<MessageStringType>;
type Props = OneMessageTypes & { id: string };
function Component(props: Props) {
  return <></>;
}

const Ua = <Component id="abc" info />; //correct
const b = <Component id="abc" success />; //correct
// const c = <Component id="abc" />; //wrong
// const d = <Component id="abc" info success />; //wrong
// 组件Component所接收的属性，有且只有一个 "info" | "success" | "warning" | "error" 中的值；

// 11.将联合类型转换为交叉类型
type UnionToIntersection<T> = (T extends any ? (t: T) => void : never) extends (
  r: infer R
) => any
  ? R
  : never;
/*
(T extends any ? ((t: T) => void) : never) 会返回多个函数联合，组成的一个类型；
每一个函数的参数类型是联合类型T中的某一项，比如这里的结果就是：
type fun1 = ((t: { a: string }) => void) | ((t: { b: string }) => void) | ((t: { c: string }) => void)
同一类型变量在反向变量位置的多个候选变量会导致推断交叉点类型，于是类型就变成了 {a:string}&{b:string}&{c:string}
*/
type UA = UnionToIntersection<{ a: string } | { b: string } | { c: string }>; //// {a: string} & {b: string} & {c: string}

// 12.取出来联合类型中的任意一个类型
type UnionPop<U> = (
  (U extends any ? (k: (x: U) => void) => void : never) extends (
    k: infer I
  ) => void
    ? I
    : never
) extends (a: infer A) => void
  ? A
  : never;
type a = 1 | 2 | 3;
type b = UnionPop<a>; // 3
type c = UnionPop<a>; // 3
type w = UnionPop<a>; // 3

type Aww = "string" | "number" | "boolean";
type Bww = Aww | any; //any
type Cww = Aww | never; //Aww
